package com.op.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.LambdaQueryWrapper;
import com.op.constants.NumberConstant;
import com.op.converter.PaperConverter;
import com.op.entity.Question;
import com.op.entity.UserQuestionData;
import com.op.mapper.QuestionMapper;
import com.op.model.req.ExamPaperReq;
import com.op.model.req.PaperDataReq;
import com.op.model.vo.ExamPaperVo;
import com.op.pojo.ExamAlgorithmParams;
import com.op.pojo.PriorityQuestion;
import com.op.service.PaperService;
import com.op.service.UserQuestionDataService;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.*;

/**
 * ClassName： PaperServiceImpl
 * Date： 2021/10/18 14:40
 *
 * @author 孤焰
 * @version 1.0
 **/

@Service
public class PaperServiceImpl implements PaperService {
    @Resource
    QuestionMapper questionMapper;
    @Resource
    UserQuestionDataService userQuestionDataService;
    @Resource
    ExamAlgorithmParams examAlgorithmParams;

    @Override
    public List<ExamPaperVo> examPaper(ExamPaperReq examPaperReq) {
        LambdaQueryWrapper<Question> lambdaQueryWrapper = new LambdaQueryWrapper<>();
        lambdaQueryWrapper.in(Question::getBankId, examPaperReq.getBankIds());
        List<Question> questionList = questionMapper.selectList(lambdaQueryWrapper);
        // 如果所选题库中的总题目数小于所需题目数量，则直接返回全部题目
        if (questionList.size() <= examPaperReq.getCount()) {
            return PaperConverter.covertQuestionToPaperVo(questionList);
        }

        // 通过计算返回全部题目
        return PaperConverter.covertQuestionToPaperVo(examAlgorithm(examPaperReq, questionList));
    }

    @Override
    public boolean submitPaper(List<PaperDataReq> paperDataReqList, Long userId) {
        for (PaperDataReq paperDataReq : paperDataReqList) {
            UserQuestionData userQuestionData = userQuestionDataService.getOneByUserIdAndQuestionId(userId, paperDataReq.getQuestionId());
            // 如果数据库中不存在该用户的答题记录，则插入数据，否则更新数据
            if (userQuestionData == null) {
                userQuestionData = new UserQuestionData();
                userQuestionData.setUserId(userId);
                userQuestionData.setQuestionId(paperDataReq.getQuestionId());
                userQuestionData.setCorrectCount(paperDataReq.getCorrectCount());
                userQuestionData.setUncertainCount(paperDataReq.getUncertainCount());
                userQuestionData.setMistakeCount(paperDataReq.getMistakeCount());
                userQuestionData.setAnswerCount(NumberConstant.ONE);
                userQuestionDataService.save(userQuestionData);
            } else {
                userQuestionData.setCorrectCount(userQuestionData.getCorrectCount() + paperDataReq.getCorrectCount());
                userQuestionData.setUncertainCount(userQuestionData.getUncertainCount() + paperDataReq.getUncertainCount());
                userQuestionData.setMistakeCount(userQuestionData.getMistakeCount() + paperDataReq.getMistakeCount());
                userQuestionData.setAnswerCount(userQuestionData.getAnswerCount() + NumberConstant.ONE);
                userQuestionData.setModifyTime(new Date());
                userQuestionDataService.updateById(userQuestionData);
            }
        }

        return true;
    }

    /**
     * 智能组卷算法（根据综合分降序排序，取前n个题目）
     * @param examPaperReq 组卷入参
     * @param questionList 题目列表
     * @return 试卷题目
     */
    private List<Question> examAlgorithm(ExamPaperReq examPaperReq, List<Question> questionList) {
        // 综合分列表
        PriorityQueue<PriorityQuestion> scoreQueue = new PriorityQueue<>(questionList.size(),
                (priorityQuestion1, priorityQuestion2) -> (int) (priorityQuestion2.getScore() - priorityQuestion1.getScore()));
        questionList.forEach(question -> {
            int matchCount = matchingTags(examPaperReq.getTags(), question.getTags());
            // 对标签分进行加和
            double tagScore = 0;
            if (matchCount == NumberConstant.ZERO) {
                tagScore += examAlgorithmParams.getTagNoMatch();
            } else {
                int notMatchCount = question.getTags().split(",").length - matchCount;
                tagScore += (examAlgorithmParams.getTagMatch() - examAlgorithmParams.getTagNotMatchSub() * notMatchCount);
                if (tagScore < examAlgorithmParams.getTagMinMatch()) {
                    tagScore = examAlgorithmParams.getTagMinMatch();
                }
            }
            // 对难度分进行加和
            double difficultyScore = 0;
            if (question.getDifficulty().equals(examPaperReq.getDifficulty())) {
                difficultyScore += examAlgorithmParams.getDifficultyMatch();
            } else if (question.getDifficulty().equals(NumberConstant.ZERO)) {
                difficultyScore += examAlgorithmParams.getDifficultyUnknown();
            } else {
                difficultyScore += examAlgorithmParams.getDifficultyNotMatch();
            }
            // 对以往数据进行加和
            double dataScore = 0;
            if (examPaperReq.getUserId() != null) {
                UserQuestionData userQuestionData = userQuestionDataService.getOneByUserIdAndQuestionId(examPaperReq.getUserId(), question.getId());
                if (userQuestionData == null) {
                    dataScore += examAlgorithmParams.getDataNoData();
                } else {
                    Integer answerCount = userQuestionData.getAnswerCount();
                    dataScore += ((double)userQuestionData.getMistakeCount() / answerCount) * examAlgorithmParams.getDataMistake()
                            + ((double)userQuestionData.getUncertainCount() / answerCount) * examAlgorithmParams.getDataUncertain()
                            + ((double)userQuestionData.getCorrectCount() / answerCount) * examAlgorithmParams.getDataCorrect();
                }
            }
            // 综合分
            double score = tagScore * examAlgorithmParams.getAllTagRate() +
                    difficultyScore * examAlgorithmParams.getAllDifficultyRate() +
                    dataScore * examAlgorithmParams.getAllDataRate();
            PriorityQuestion priorityQuestion = new PriorityQuestion(question, score);
            scoreQueue.add(priorityQuestion);
        });

        List<Question> res = new ArrayList<>(examPaperReq.getCount());
        for (int i = 0; i < examPaperReq.getCount(); i++) {
            if (!scoreQueue.isEmpty()) {
                res.add(scoreQueue.poll().getQuestion());
            } else {
                break;
            }
        }
        return res;
    }

    /**
     * 计算当前入参tagReq与数据库中tagDb的tag匹配个数
     * @param tagReqList 前端选中的tag
     * @param tagsDb 数据库中存储的tag
     * @return 匹配个数
     */
    private int matchingTags(List<String> tagReqList, String tagsDb) {
        int count = 0;
        Set<String> set = new HashSet<>(tagReqList.size());
        tagReqList.forEach(tagReq -> set.add(tagReq.toLowerCase()));
        String[] tagsDbList = tagsDb.split(",");
        for (String tagDb : tagsDbList) {
            if (set.contains(tagDb.toLowerCase())) {
                ++count;
            }
        }
        return count;
    }
}
